package cn.pm.datasource.sharding;


import cn.pm.datasource.sharding.bean.AutoCreateTablesCacheBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.core.yaml.config.sharding.YamlShardingStrategyConfiguration;
import org.apache.shardingsphere.core.yaml.config.sharding.YamlTableRuleConfiguration;
import org.apache.shardingsphere.core.yaml.config.sharding.strategy.YamlStandardShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.jdbc.core.datasource.ShardingDataSource;
import org.apache.shardingsphere.shardingjdbc.spring.boot.sharding.SpringBootShardingRuleConfigurationProperties;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Create by spf on 2019/11/28
 * <p>
 * 自动加载 需要自动创建表的 逻辑表--真实表 缓存
 */

@Slf4j
@Component
@Order(-1)
public class LoadAutoCreateTablesCacheRunner implements ApplicationRunner {

    /**
     * 需要动态创建的表名称
     */
    @Resource
    private ShardingDataSource dataSource;
    @Resource
    private SpringBootShardingRuleConfigurationProperties shardingRuleConfigurationProperties;

    /**
     * 项目启动时，即将需要自动创建表的已有逻辑表  加入到内存缓存中
     */
    public void run(ApplicationArguments args) throws Exception {

        List<AutoCreateTablesCacheBean> list = new ArrayList<>();

        // 循环每一个Sharding配置
        Map<String, YamlTableRuleConfiguration> tables = shardingRuleConfigurationProperties.getTables();
        for (String logicTable : tables.keySet()) {
            YamlTableRuleConfiguration tableRuleConfiguration = tables.get(logicTable);
            if (tableRuleConfiguration == null)
                continue;

            YamlShardingStrategyConfiguration tableStrategy = tableRuleConfiguration.getTableStrategy();
            if (tableStrategy == null)
                continue;
            YamlStandardShardingStrategyConfiguration standard = tableStrategy.getStandard();
            if (standard == null)
                continue;

            // 实际数据节点
            String actualDataNodes = tableRuleConfiguration.getActualDataNodes();
            if (StringUtils.isBlank(actualDataNodes))
                continue;
            String[] node = actualDataNodes.split("\\.");
            if (node.length != 2) {
                log.error("自动建表，配置文件分析失败：{}", actualDataNodes);
                continue;
            }

            String preciseAlgorithmClassName = standard.getPreciseAlgorithmClassName();
            String rangeAlgorithmClassName = standard.getRangeAlgorithmClassName();
            // = in 的路由规则
            // 如果是我们自定义的路由规则，则说明是需要自动创建表的
            if (StringUtils.isNotBlank(preciseAlgorithmClassName) && preciseAlgorithmClassName.startsWith("cn.pm.datasource.sharding.algorithm")) {
                AutoCreateTablesCacheBean bean = new AutoCreateTablesCacheBean();
                bean.setDataSourceName(node[0].trim());
                bean.setLogicTableName(node[1].trim());
                list.add(bean);
            }
            // > < between...and 的路由规则
            else if (StringUtils.isNotBlank(rangeAlgorithmClassName) && rangeAlgorithmClassName.startsWith("cn.pm.datasource.sharding.algorithm")) {
                AutoCreateTablesCacheBean bean = new AutoCreateTablesCacheBean();
                bean.setDataSourceName(node[0].trim());
                bean.setLogicTableName(node[1].trim());
                list.add(bean);
            }
        }

        if (list.isEmpty())
            return;

        // 根据配置读取所有表名
        for (AutoCreateTablesCacheBean autoCreateTablesCacheBean : list) {
            DataSource dataSource = this.dataSource.getDataSourceMap().get(autoCreateTablesCacheBean.getDataSourceName());
            if (dataSource == null)
                throw new Exception(autoCreateTablesCacheBean.getDataSourceName() + "数据源不存在");
            List<String> allTableNames = ShardingTableNameUtils.getAllTableNames(dataSource, autoCreateTablesCacheBean.getLogicTableName() + "_____%");
            autoCreateTablesCacheBean.setRealTableNameList(allTableNames);

            // 将现有的所有数据放入到工具类中
            ShardingTableNameUtils.addTable(autoCreateTablesCacheBean.getLogicTableName(), autoCreateTablesCacheBean);
        }
    }
}
